package com.unibeta.cloudtest.mesher.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import com.unibeta.cloudtest.CloudTestOutput;
import com.unibeta.cloudtest.CloudTestService;
import com.unibeta.cloudtest.config.CloudTestCase;
import com.unibeta.cloudtest.mesher.auth.context.AuthContextHolder;
import com.unibeta.cloudtest.mesher.config.CloudTestBootStrap;
import com.unibeta.cloudtest.mesher.config.ConfigurationProxy;
import com.unibeta.cloudtest.mesher.dto.CloudTest;
import com.unibeta.cloudtest.mesher.dto.Option;
import com.unibeta.cloudtest.mesher.dto.TreeModel;
import com.unibeta.cloudtest.mesher.modules.plugin.ServicePlugin;
import com.unibeta.cloudtest.mesher.service.EditorService;
import com.unibeta.cloudtest.restful.RemoteRESTfulTestServiceProxy;
import com.unibeta.cloudtest.tool.CloudTestReportor;
import com.unibeta.cloudtest.util.CloudTestUtils;
import com.unibeta.cloudtest.util.ObjectDigester;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;

@Service
public class EditorServiceImpl implements EditorService {

	@Override
	public List<TreeModel> getFileTree(String rootPath) throws Exception {

		List<TreeModel> list = new ArrayList<TreeModel>();

		if (StringUtils.isBlank(rootPath)) {
			return list;
		}

		File[] files = new File(rootPath).listFiles();

		if (files == null) {
			return list;
		}

		for (File f : files) {
			if (f.getName().endsWith(".bin")) {
				continue;
			}

			TreeModel node = new TreeModel();
			node.setId(CloudTestUtils.getContextedURI(f.getCanonicalPath()));
			node.setLabel(f.getName());
			node.setLeaf(f.isFile());

			if (f.isFile()) {
				// node.setContent(CloudTestUtils.readFileContent(f.getCanonicalPath()));
			}
			if (f.isDirectory()) {
				node.setChildren(this.getFileTree(f.getCanonicalPath()));
			}
			list.add(node);
		}

		return list;
	}

	@Override
	@CacheEvict(value = "$gcache$", key = "#filePath")
	public void save(String content, String filePath) throws Exception {
		
		if (new File(filePath).isFile()) {
			if (filePath.toLowerCase().endsWith(".ms.xml")) {
				content = content.replace("<cloudtest", "<cloudnative").replace("cloudtest>", "cloudnative>")
						.replace("<testCase", "<service").replace("testCase>", "service>");
			}
			
			IOUtils.write(content, new FileOutputStream(new File(filePath)));
//			XmlUtils.prettyPrint(content, filePath);
		}

	}

	@Override
	@Cacheable(value = "$gcache$", key = "#filePath")
	public CloudTest load(String filePath) throws Exception {
		CloudTest cloudtest = new CloudTest();
		String content = CloudTestUtils.readFileContent(filePath);
		List<Option> caseList = new ArrayList<Option>();

		try {
			CloudTestCase testcase = ConfigurationProxy.loadCloudTestCase(filePath, null);
			testcase.testCase.stream().forEach(c -> {
				if (!"true".equalsIgnoreCase(c.ignore)) {
					Option op = new Option();
					op.setLabel(c.id);
					op.setValue(c.id);

					caseList.add(op);
				}
			});
			cloudtest.setRunnable(true);
		} catch (Exception e) {
			cloudtest.setRunnable(false);
		}

		cloudtest.setContent(content);
		cloudtest.setCaseList(caseList);

		return cloudtest;
	}

	@Override
	public CloudTest run(CloudTest cloudtest) throws Exception {

		Assert.isTrue(cloudtest != null, "cloudtest request is null");
		CloudTestOutput output = null;

		StringBuffer cases = new StringBuffer();

		if (cloudtest.getCases() != null && cloudtest.getCases().length > 0) {
			Arrays.asList(cloudtest.getCases()).stream().forEach(c -> {
				cases.append(c + ",");
			});
		}

		String caseUri = cloudtest.getCaseUri();
		if (cloudtest.getCaseUri().toLowerCase().endsWith(".xml")) {
			caseUri = cloudtest.getCaseUri() + "@[" + cases.toString() + "]";
		}

		URL url = null;
		if (!StringUtils.isBlank(cloudtest.getEndpoint())) {
			url = new URL(cloudtest.getEndpoint());
		}

		Map<String, Object> ctx = new HashMap<String, Object>();
//		
//		Configuration.setVRULES_HOME(ConfigurationProxy.getCloudTestRootPath());
//		Configuration.setThreadlocalEnabled(true);
//		
//		ctx.put("$vrules$", XEngineFactory.getRuleEngine());
//		ctx.put("$rating$", XEngineFactory.getRatingEngine());

		String names[] = CloudTestBootStrap.getApplicationContext().getBeanNamesForType(ServicePlugin.class);
		Arrays.asList(names).forEach(n -> {
			ServicePlugin plugin = (ServicePlugin) CloudTestBootStrap.getApplicationContext().getBean(n);
			plugin.plugin(ctx);
		});

		if (url == null || ConfigurationProxy.getAPIHost().contains(url.getHost() + ":" + url.getPort())) {
			output = new CloudTestService().doTest(caseUri, ctx);
		} else {

			output = new RemoteRESTfulTestServiceProxy(cloudtest.getEndpoint().trim()).doTest(caseUri, ctx);
		}

		if (cloudtest.isReport()) {
			CloudTestReportor.sendReport(null, "default", output);
		}

		if (output != null && output.getTestCaseResults() != null) {
			output.getTestCaseResults().stream().forEach(r -> {
				String json = ObjectDigester.toJSON(ObjectDigester.fromXML(r.getReturns()));// .replace("\"", "`");
				r.setReturns(json);
			});
		}
		cloudtest.setResult(StringEscapeUtils.unescapeXml(ObjectDigester.toXML(output)));
		cloudtest.setReportUrl(ConfigurationProxy.getAPIHost() + "/workspace/"
				+ AuthContextHolder.getContext().getCurrentUserDetails().getUsername()
				+ "/reports/default/html/latest.html");

		return cloudtest;
	}

	@Override
	public void newFile(String filePath) throws Exception {

		File file = new File(filePath);
		if (file.exists()) {
			throw new Exception(filePath + " already exists");
		}

		if (!file.isFile()) {
			// throw new Exception(filePath + " is not a valid file name");
		}

		CloudTestUtils.checkFile(filePath);

		file.createNewFile();
	}

	@Override
	public void newFolder(String filePath) throws Exception {
		File file = new File(filePath);

		if (file.exists()) {
			throw new Exception(filePath + " already exists");
		}

		if (!file.isDirectory()) {
			// throw new Exception(filePath + " is not a valid folder name");
		}

		CloudTestUtils.checkFile(filePath);
		file.mkdirs();
	}

	@Override
	@CacheEvict(value = "$gcache$", key = "#filePath")
	public void deleteFile(String filePath) throws Exception {
		CloudTestUtils.deleteFiles(filePath);
		new File(filePath).delete();

	}

	@Override
	@CacheEvict(value = "$gcache$", allEntries = true)
	public void restoreWorkspace(String username) throws Exception {

		String cloudtestHome = ConfigurationProxy.getWorkspace() + "/" + username  + "/";
		CloudTestUtils.deleteFiles(cloudtestHome + "cloudtest");
		CloudTestUtils.checkFile(cloudtestHome + "cloudtest");
		InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("cloudtest.zip");

		File zipFile = new File(cloudtestHome + "cloudtest.zip");
		OutputStream out = new FileOutputStream(zipFile);

		IoUtil.copy(in, out);
		CloudTestUtils.unzipFiles(zipFile);
		out.close();
		zipFile.delete();
	}

	@Override
	@CacheEvict(value = "$gcache$", allEntries = true)
	public String importFile(String basePath, MultipartFile file) throws Exception {

		if (file != null) {
			String destFilePath = basePath + "/" + file.getOriginalFilename();
			CloudTestUtils.checkFile(destFilePath);
			File destFile = new File(destFilePath);
			destFile.delete();
			file.transferTo(destFile);

			if (basePath.equals(ConfigurationProxy.getCLOUDTEST_HOME())) {
				CloudTestUtils.deleteFiles(basePath + "/cloudtest/");
				CloudTestUtils.unzipFiles(destFile);
				destFile.delete();
			}

			return destFilePath;
		} else {
			return null;
		}
	}

	@Override
	public String duplicateFile(String filePath) throws Exception {

		File file = new File(filePath);

		if (file.isFile()) {
			InputStream input = new FileInputStream(file);
			int lastIndexOf = file.getName().lastIndexOf(".");
			String destName = file.getName().substring(0, lastIndexOf) + "-Copy"
					+ file.getName().substring(lastIndexOf);

			File destFile = new File(file.getParent() + "/" + destName);
			OutputStream output = new FileOutputStream(destFile);

			IOUtils.copy(input, output);
			input.close();
			output.close();

			return destFile.getCanonicalPath();
		} else if (file.isDirectory()) {
			throw new Exception("'" + filePath + "' is not a valid file to be duplicated.");
		}

		return null;
	}

	@Override
	public String renameFile(String filePath, String newName) throws Exception {

		File file = new File(filePath);
		File renamedFile = new File(file.getParent() + "/" + newName);
		file.renameTo(renamedFile);

		return renamedFile.getCanonicalPath();
	}

	@Override
	public String exportFile(String basePath) throws Exception {
		File file = new File(basePath);

		String name = file.getName();
		int lastIndexOf = name.lastIndexOf(".");

		String zipName = lastIndexOf > 0 ? name.substring(0, lastIndexOf) : name;
		String zipFileName = file.getParent() + "/" + zipName + ".zip";

		File zipFile = new File(zipFileName);
		if (zipFile.exists()) {
			zipFile.delete();
		}

		String username = AuthContextHolder.getContext().getCurrentUserDetails().getUsername();
		File zipedFile = CloudTestUtils.zipFiles(file);
		File dest = new File(file.getParent() + "/export/" + UUID.randomUUID().toString()
				+ "/natimesh-sources-" + username + "@" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".zip");
		FileUtil.copy(zipedFile, dest, true);
		
		if (zipedFile.exists()) {
			zipedFile.delete();
		}

		return CloudTestUtils.wrapFilePath(dest.getCanonicalPath());
	}

	@Override
	@CacheEvict(value = "$gcache$", allEntries = true)
	public void clearCache() throws Exception {
		
	}

}
